<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>父子组件通信-数据双向绑定-props+自定义事件实现</title>
</head>
<body>
<!--
双向绑定逻辑：
1、初步定义模板（指定模板id）
2、构造组件对象（组件对象template属性指定模板id，props添加属性）
3、将子组件注册进父组件，也就是父组件的components属性指定组件名和组件对象
3、构造组件对象的data属性绑定props添加的属性，这样从父组件传到props属性上的值，就会直接和data属性的值进行绑定（也就是数据父传子），
组件内部直接使用data属性中对象的属性的值即可
4、构造组件内部事件监听，实现数据的子传父，比如：组件内部有个输入框，这个输入框的值绑定到了组件data的属性上，
也就是上一步所说的使用data属性即是父传子的数据，这时给输入框添加输入事件监听，在输入事件调用的方法中自定义一个事件，
并且将输入的值传进去，拿给父组件去监听这个自定义事件，父组件监听到自定义事件的变化，拿到这个输入的值去更新自己data中的响应的属性即可，
这样子组件数据的变化就更新了父组件响应的数据。（也就是数据子传父）
5、父组件监听子组件自定义的事件，拿到子组件传过来的值更新自己data属性中对应的对象的属性值
这样就实现了父子组件的数据的双向绑定，利用数据的父传子-props，数据的子传父-自定义事件即可实现父子组件的数据的双向绑定。
-->

<div id="app">
  <my-div :number1="num1" :number2="num2" @number1-change="num1Change" @number2-change="num2Change"/>
</div>

<template id="myDiv">
  <div>

    <p style="color: red">
      <span style="color: blue">效果说明：</span>number1是number2的100倍，当输入number1的值时候，number2的值也跟着变化，并且number2是number1的100倍；
      <br>当输入number2的时候，number1的值也跟着变化，number1是number2的1/100<br>
      <span style="color: blue">参数说明：</span>含有props字体的是对应的父组件相应参数，含有data的是子组件对应的参数值<br>
    </p>

    <p>props number1: {{number1}}</p>
    <p>data dNumber1: {{dNumber1}}</p>
    输入dNumber1的值：<input @input="number1Input" :value="dNumber1">

    <!--    不要用v-model直接绑定props中的number1，虽然效果是子组件number1的值改变了，父组件的num1也会改变，
            看似也是实现了双向绑定，但是实际控制台已经报错了-->
    <!--    <input v-model="number1">-->

    <p>props number2: {{number2}}</p>
    <p>data dNumber2: {{dNumber2}}</p>
    输入dNumber2的值：<input @input="number2Input" :value="dNumber2">
    <!--    <input v-model="number2">-->
  </div>
</template>
<script src="../js/vue.js"></script>
<script>

  //【构造子组件对象（vue实例的子组件）】
  const myDiv = {
    template: "#myDiv", //绑定需要注册的组件
    props: { //添加属性，给组件添加标签属性，用对象来定义多个组件标签属性，对象的属性名称也就是组件标签属性
      number1: Number,
      number2: Number
    },
    data() { // 组件要绑定的数据
      return {
        dNumber1: this.number1,
        dNumber2: this.number2,
      }
    },
    methods: { //组件调用的方法
      number1Input(event) {
        if(event.target.value != undefined && event.target.value.trim() != ""){
          this.dNumber1 = parseFloat(event.target.value);
          //自定义事件，并且传入默认值，这里是定义了一个number1-change事件，并且默认传入dNumber1值，向上传递，由父组件监听使用
          this.$emit("number1-change", this.dNumber1)
          //自定义事件，并且传入默认值，这里是定义了一个number2-change事件，并且默认传入dNumber2值，向上传递，由父组件监听使用
          this.dNumber2 = this.dNumber1 * 100
          this.$emit("number2-change", this.dNumber2)
        }
      },
      number2Input(event) {
        if(event.target.value != undefined && event.target.value.trim() != ""){
          this.dNumber2 = parseFloat(event.target.value);
          //自定义事件，并且传入默认值，这里是定义了一个number2-change事件，并且默认传入dNumber2值，向上传递，由父组件监听使用
          this.$emit("number2-change", this.dNumber2)
          this.dNumber1 = this.dNumber2 / 100;
          //自定义事件，并且传入默认值，这里是定义了一个number1-change事件，并且默认传入dNumber1值，向上传递，由父组件监听使用
          this.$emit("number1-change", this.dNumber1)
        }
      }
    }
  }

  //根组件vue实例（也是myDiv组件的父组件）
  const app = new Vue({
    el: "#app", // 用于挂载要管理的元素，类似于jquery中$("#"),找到要操作的DOM节点
    data: { // 定义的数据，这些定义的数据，只在el 挂在的元素标签的内部才能使用解析
      num1: 1,
      num2: 0
    },
    // 【局部组件注册】
    // 注册组件，components属性，传入对象，对象的属性为组件的名称，值为构造的组件对象
    components: {
      myDiv //注册组件，这是es6增强写法，同名可省略，相当于myDiv : myDiv
    },
    methods: { // 定义的方法，这些定义的方法也是只有在el 挂在的元素标签的内部才能使用解析
      num1Change(number) {
        this.num1 = number;
      },
      num2Change(number) {
        this.num2 = number;
      },
    }
  });
</script>
</body>
</html>